package com.ruoyi.aitseeyon.service;

import java.io.File;
import java.sql.Date;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONArray;
import com.ruoyi.aitcommon.vo.AttachmentVO;
import com.ruoyi.aitseeyon.vo.*;
import com.ruoyi.common.core.redis.RedisCache;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.aitcommon.anotation.ServiceLog;
import com.ruoyi.aitcommon.service.IAitEntityInfoService;
import com.ruoyi.aitcommon.service.SQLService;
import com.ruoyi.aitcommon.service.TransService;
import com.ruoyi.aitcommon.utils.EntityCache;
import com.ruoyi.aitcommon.utils.ExceptionUtils;
import com.ruoyi.aitcommon.utils.LogUtils;
import com.ruoyi.aitcommon.utils.ObjUtils;
import com.ruoyi.aitcommon.utils.RestServiceUtils;
import com.ruoyi.aitcommon.utils.SQLUtils;
import com.ruoyi.aitcommon.utils.XmlUtils;
import com.ruoyi.aitcommon.vo.AggMap;
import com.ruoyi.aitseeyon.utils.ZYOAFieldTransUtils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

/**
 * OA接口的封装类
 * 
 * @author Administrator
 *
 */
@Service
public class ZYOARestService {
	@Autowired
	IAitEntityInfoService infoService;
	@Autowired
	ZYOAFieldTransUtils transUtils;
	@Autowired
	TransService transservice;
	@Autowired
	EntityCache entityCache;
	@Autowired
	SQLService sqlService;
	@Autowired
	RedisCache redisCache;

	// REST用户登录名
	private String userName = "陈黎";
	// REST用户密码
	private String password = "bc2ad1e7-3de9-4f85-b476-78c8f4c57b76";
	private String URL = "http://222.209.86.43:7788";
	private String loginName = "demo";
//	private String token = null;//这里不再记录,通过参数传入,避免并发
	private String loginType="";
//	设置接口参数
	private void initParm(Map<String, Object> interParm) {
		this.userName = ObjUtils.toString(interParm.get("userName"));
		this.password = ObjUtils.toString(interParm.get("password"));
		this.URL = ObjUtils.toString(interParm.get("url"));
		this.loginName = ObjUtils.toString(interParm.get("loginName"));
		this.loginType=ObjUtils.toString(interParm.get("loginType"));
	}

	/**
	 * 调用OA接口,产生token
	 * @param interParm 包含 username,password,loginname等信息
	 * @return token,并记录在对象属性token中
	 */
	public String createToken(Map<String, Object> interParm,JSONObject oadb) throws Exception {
		this.initParm(interParm);
		if(ObjUtils.isNotBlank(loginType)){
//			登陆账号进行翻译 人员编码/人员姓名->登录名,interParm中需要配置"loginType"
			final Object transName = transUtils.loginTrans(loginType, loginName, oadb);
			this.loginName=transName.toString();
		}
		JSONObject json = new JSONObject();
		json.put("userName", userName);
		json.put("password", password);
		json.put("loginName", loginName);
		String url=URL + "/seeyon/rest/token";
//		调用api,获取token
		JSONObject retjson = RestServiceUtils.post(url,null, json);
//		处理返回
		if (StringUtils.isBlank(retjson.getString("id"))) {
			String msg = "OA获取token失败!,错误:" + retjson.getString("message");
			throw new Exception(msg);
		}
		final String tokenid = retjson.getString("id");
//		this.token = tokenid;//这里不再设置,放在外部设置
		return tokenid;
	}

	/**
	 * 构造OA Form接口数据Map
	 * 这里还没有转换为XML
	 * @param datas Map类型数据
	 * @param id    表单的summary id 该参数作废,可以传""
	 * @param name  表单的summary name
	 * @return OA接口数据Map
	 */
	private Map<String, Object> createOAFormData(List<Map<String, Object>> datas, String id, String name) {
//		OA Form接口数据的对象形式
		ZYOAFormsVO mainvo = new ZYOAFormsVO();

		for (Map<String, Object> map : datas) {
			ZYOAFormExportVO datavo = new ZYOAFormExportVO();
			ZYOAColumnVO sum = new ZYOAColumnVO();
			sum.$id = id;
			sum.$name = name;
			datavo.summary = sum;

			for (String key : map.keySet()) {
				if (map.get(key) == null)
					continue;
				Object value = map.get(key);
				String valstr = value.toString();
				if (value instanceof Timestamp) {
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
					valstr = sdf.format(value);
				} else if (value instanceof Date) {
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
					valstr = sdf.format(value);
				}
				datavo.addValue("", key, valstr);
			}
			mainvo.addForm(datavo);
		}
//		将对象转换为MAP
		return (JSONObject) JSON.toJSON(mainvo);
	}

	/**
	 * 构造OA Form接口数据Map
	 * 
	 * @param aggvo aggMap 数据
	 * @param maintable 主表表名
	 * @param sontable 子表表名
	 * @param entityName 模板编码
	 */
	private Map<String, Object> getProcessBody(AggMap aggvo, String maintable
			, String sontable, String entityName,List<String> attachments,List<String> sontableEx)
			throws Exception {
//		OA Form接口数据的对象形式
		ZYOAProcessVO mainvo = new ZYOAProcessVO();
		ZYOAProcessData data = new ZYOAProcessData();

//		表单整体数据
		Map<String, Object> formdata = new LinkedHashMap<>();
//		表单main数据
		if (aggvo.getHeadVO() == null || StringUtils.isBlank(maintable))
			throw new Exception("表单数据为空或表单名称为空");
		formdata.put(maintable, aggvo.getHeadVO());
//		表单son数据
		if (aggvo.getBodyVOS() != null && StringUtils.isNotBlank(sontable)) {
			formdata.put(sontable, aggvo.getBodyVOS());
		}
//		更多子表处理
		if(sontableEx!=null&&sontableEx.size()>0){
			for (String tablename : sontableEx) {
				if(!aggvo.containsKey(tablename))
					ExceptionUtils.throwException(null,"aggvo中不包含子表:[{}]",tablename);
				formdata.put(tablename,aggvo.get(tablename));
			}
		}
//		其他数据
		formdata.put("thirdAttachments", new ArrayList<>());

		data.templateCode = entityName;//模板名称
		data.attachments=attachments;//附件,这里是表头附件的传入
		data.data = formdata;
		mainvo.data = data;
//		将对象转换为MAP
		return (JSONObject) JSON.toJSON(mainvo);
	}

	/**
	 * 上传OA附件
	 * @param data 传入的aggvo
	 * @param pathList 传入文件Path集合
	 * @return 文件信息VO
	 */
	private List<ZYOAFileInfoVO> fileUpload(AggMap data,List<String> pathList,Map<String,Object> interParm) throws Exception {
		String url = URL + "/seeyon/rest/attachment?token=%s";
//		使用外部的token
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		url = String.format(url, token);

		List<ZYOAFileInfoVO> retlist=new ArrayList<>();
		for (String path : pathList) {
			final File file = FileUtil.file(path);
			if(!file.exists()){
				ExceptionUtils.throwException(null,"文件不存在,文件地址:[{}]",path);
			}
			String retstr=RestServiceUtils.postFile(url,null,"file",file);
			JSONObject retjson= JSONObject.parseObject(retstr);
			final JSONArray atts = retjson.getJSONArray("atts");
			final String fileUrl = atts.getJSONObject(0).getString("fileUrl");

			//		构造返回值
			ZYOAFileInfoVO retvo = new ZYOAFileInfoVO();
//			retvo.setEntityCode(dataCode.toString());
//			retvo.setEntityName(formName.toString());
			retvo.setFileName(file.getName());
			retvo.setFilePath(file.getAbsolutePath());
			retvo.setFileUrl(fileUrl);
			retlist.add(retvo);
		}
		return retlist;
	}


	/**
	 * 获取OA底表附件,存储附件,并写入底表url
	 */
	@ServiceLog("OA获取附件")
	public List<Map<String,Object>> fileDownload(List<Map<String, Object>> datas, JSONObject oadb, Map<String, Object> context,
			Map<String, Object> interParm, Map<String, Object> entityInfo) throws Exception {
//		从缓存过滤重复数据
		List<Map<String, Object>> filterlist = entityCache.filterEntityChange(entityInfo, datas);
//		初始化参数及token
		final String token = this.createToken(interParm, oadb);
		interParm.put("token",token);//通过interParm传递token

		List<Map<String,Object>> retlist = new ArrayList<>();
		for (Map<String, Object> data : filterlist) {
			try {
				ZYOAFileInfoVO retvo = fileDownloadOne(data, oadb, context, interParm, entityInfo);
//				没有附件则不用处理
				if(retvo==null)
					continue;
//				记录实体信息
				entityInfo.put("oper", "新增");
				infoService.insertAitEntityInfo(entityInfo, data, null);
				
				retlist.add((JSONObject)JSONObject.toJSON(retvo));
			} catch (Exception ex) {
//				ex.printStackTrace();
				entityInfo.put("oper", "新增");
				infoService.insertAitEntityInfo(entityInfo, data, ex);
//					retlist.add(err);
			}
		}
		return retlist;
	}



	private ZYOAFileInfoVO fileDownloadOne(Map<String, Object> data, JSONObject oadb, Map<String, Object> context,
			Map<String, Object> interParm, Map<String, Object> entityInfo) throws Exception {
		Object formName = context.get("formname");
		ExceptionUtils.checkBlankOrUndefined(formName, "formname必填");
		Object idField = context.get("idfield");
		ExceptionUtils.checkBlankOrUndefined(idField, "idfield必填");
		Object nameField = context.get("namefield");
		ExceptionUtils.checkBlankOrUndefined(nameField, "namefield必填");
		Object saveDir = context.get("savedir");
		ExceptionUtils.checkBlankOrUndefined(saveDir, "savedir必须填写");
		Object fileUrl = context.get("fileurl");
		ExceptionUtils.checkBlankOrUndefined(fileUrl, "fileurl必须填写");
		Object codeField = context.get("codefield");
		ExceptionUtils.checkBlankOrUndefined(codeField, "codefield必须填写");

//		文件id
		Object fileid = data.get(idField.toString());
//		文件名称
		Object filename = data.get(nameField.toString());
//		数据的编码
		Object dataCode = data.get(codeField.toString());
		if(StrUtil.isBlankIfStr(fileid)||StrUtil.isBlankIfStr(filename)||StrUtil.isBlankIfStr(dataCode)) {
			return null;
		}
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		String url = URL + "/seeyon/rest/attachment/file/%s?token=%s";
		url = String.format(url, fileid, token);
//		存储目录:目录\对象名\code\文件名
		String path = "%s\\%s\\%s\\%s";
		path = String.format(path, saveDir, formName, dataCode, filename);
//		返回URL:fileurl/对象名/code/文件名
		String returl = "%s/%s/%s/%s";
		returl = String.format(returl, fileUrl, formName, dataCode, filename);

//		远程调用
		LogUtils.info("OA调用地址:{}", url);
		LogUtils.info("OA调用token:{}", token);
		File ret = RestServiceUtils.getFile(url, path);
		LogUtils.info("OA调用返回:{}", ret.getAbsolutePath());

//		构造返回值
		ZYOAFileInfoVO retvo = new ZYOAFileInfoVO();
		retvo.setEntityCode(dataCode.toString());
		retvo.setEntityName(formName.toString());
		retvo.setFileName(filename.toString());
		retvo.setFileUrl(returl);
		return retvo;
	}

	/**
	 * 底表单条新增
	 * 这里没有重复获取token,使用之前批量获取的token
	 * @param data Map数据
	 * @param context 调用上下文
	 * @param interParm 调用接口
	 * @param oadb DB链接
	 */
	public String formImportOne(Map<String, Object> data, Map<String, Object> context, Map<String, Object> interParm,
			JSONObject oadb) throws Exception {
//		构造Body
		JSONObject json = getFormBody(data,null,context,oadb);
//		接口调用
		String entityName = context.get("entityname").toString();
		String url = URL + "/seeyon/rest/form/import/" + entityName;
		Map<String, Object> head = new HashMap<>();
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		head.put("token", token.toString());
		String ret = RestServiceUtils.post(url, head, json.toJSONString());
//		校验返回
		if (!ret.equals("1"))
			throw new Exception("OA无流程表单导入失败:" + ret);
		return ret;
	}

	/**
	 * 构造Form的Body数据
	 * @param data 数据
	 * @param id id
	 * @param context 上下文
	 * @param oadb OA链接
	 */
	private JSONObject getFormBody(Map<String, Object> data,Object id,Map<String, Object> context,JSONObject oadb)
			throws Exception {
		String summaryName = context.get("formname").toString();
		ExceptionUtils.checkBlankOrUndefined(summaryName, "context.formname为空");
		String entityName = context.get("entityname").toString();
		ExceptionUtils.checkBlankOrUndefined(entityName, "context.entityname为空");

//		深度拷贝,避免修改data数据
		Map<String,Object> data1 = ObjUtils.deepClone(data,data.getClass());
//		数据翻译器
		Object transmap = context.get("transmap");
		if (ObjectUtil.isNotEmpty(transmap)) {
			List<Map<String, Object>> trans = transUtils.DataTrans(Arrays.asList(data1), (Map<String, Object>) transmap, oadb);
			data1 = trans.get(0);
		}
//		构造参数map
		Map<String, Object> param = this.createOAFormData(Arrays.asList(data1), "", summaryName);
//		将map转化为xml
		String dataXml = XmlUtils.Map2Xml("forms", param).asXML();

//		接口赋值
		JSONObject json = new JSONObject();
		if(id!=null) json.put("moduleId",id.toString());
		json.put("templateCode",entityName);
		json.put("loginName", loginName);
		json.put("dataXml", dataXml);
		return json;
	}

	/**
	 * 构造OA Form接口Body参数
	 * @param data Map类型数据
	 * @param id 记录ID
	 * @param context 调用上下文
	 * @param oadb OA DB链接
	 * @return OA接口数据Map
	 */
	private JSONObject getFormBodyEx(Map<String, Object> data,Object id,Map<String, Object> context,JSONObject oadb)
			throws Exception {
		String formName = context.get("formname").toString();
		ExceptionUtils.checkBlankOrUndefined(formName, "context.formname为空");
		String entityName = context.get("entityname").toString();
		ExceptionUtils.checkBlankOrUndefined(entityName, "context.entityname为空");
		String rightId=null;
		if(context.containsKey("rightid"))
			rightId = context.get("rightid").toString();
		ExceptionUtils.checkBlankOrUndefined(rightId, "context.rightid为空");

//		深度拷贝,避免修改data数据
		Map data1 = ObjUtils.deepClone(data,data.getClass());
//		数据翻译器
		Object transmap = context.get("transmap");
		if (ObjectUtil.isNotEmpty(transmap)) {
			List<Map<String, Object>> trans = transUtils.DataTrans(Arrays.asList(data1), (Map<String, Object>) transmap, oadb);
			data1 = trans.get(0);
		}

//		OA Form接口数据的对象形式
		ZYOAFormVOEx formVOEx=new ZYOAFormVOEx();
		formVOEx.formCode=entityName;
		formVOEx.loginName=this.loginName;
		formVOEx.rightId=rightId;
		formVOEx.dataList=new ArrayList<>();
//		构造主子表
		ZYOAFormVOEx.AggTable aggTable=formVOEx.new AggTable();
		formVOEx.dataList.add(aggTable);
		ZYOAFormVOEx.Table table1=formVOEx.new Table();
		aggTable.masterTable=table1;
		table1.name=formName;
//		构造主表记录
		ZYOAFormVOEx.Record record=formVOEx.new Record();
		if(id!=null) record.id=id.toString();
		table1.record=record;
		record.fields=new ArrayList<>();
//		构造主表字段
		for(String key:data.keySet()){
			if(data.get(key)==null) continue;
			//循环字段
			ZYOAFormVOEx.Field field=formVOEx.new Field();
			field.name=key;
			field.value=data.get(key).toString();
			record.fields.add(field);
		}

//		将对象转换为MAP
		return (JSONObject) JSONObject.toJSON(formVOEx);
	}

	/**
	 * 底表单条新增
	 * 调用底表新接口 https://www.kancloud.cn/cap4_development/cap-doc/1808152
	 * 这里没有重复获取token,使用之前批量获取的token
	 * @param data Map数据
	 * @param context 调用上下文
	 * @param interParm 调用接口
	 * @param oadb DB链接
	 */
	public String formImportOneEx(Map<String, Object> data, Map<String, Object> context, Map<String, Object> interParm,
								JSONObject oadb) throws Exception {
//		获取BODY
		final JSONObject josn = getFormBodyEx(data,null,context,oadb);
//		接口调用
		String entityName = context.get("entityname").toString();
		String url = URL + "/seeyon/rest/cap4/form/soap/batch-add";
		Map<String, Object> head = new HashMap<>();
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		head.put("token", token.toString());
		String retstr = RestServiceUtils.post(url, head, josn.toJSONString());
//		校验返回
		JSONObject retjson=JSONObject.parseObject(retstr);
		if (retjson.getInteger("code")!=0)
			throw new Exception("OA无流程表单导入失败:" + retjson.getString("message"));
		return retstr;
	}

	/**
	 * 底表单条修改
	 * 这里没有重复获取token,使用之前批量获取的token
	 * @param data 	数据
	 * @param id	表单ID
	 * @param context	上下文
	 * @param interParm	调用链接
	 * @param oadb	数据库DB
	 */
	public String formUpdateOneEx(Map<String, Object> data,Object id, Map<String, Object> context
			, Map<String, Object> interParm, JSONObject oadb) throws Exception {
		final JSONObject body = this.getFormBodyEx(data,id, context,oadb);
//		接口调用
		String url = URL + "/seeyon/rest/cap4/form/soap/batch-update";
		Map<String, Object> head = new HashMap<>();
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		head.put("token", token.toString());
		String retstr = RestServiceUtils.post(url,head,body.toJSONString());
//		校验返回
		JSONObject retjson=JSONObject.parseObject(retstr);
		if (retjson.getInteger("code")!=0)
			throw new Exception("OA无流程表单导入失败:" + retjson.getString("message"));
		return retstr;
	}

	/**
	 * 底表单条修改
	 * 这里没有重复获取token,使用之前批量获取的token
	 * @param data 	数据
	 * @param id	表单ID
	 * @param context	上下文
	 * @param interParm	调用链接
	 * @param oadb	数据库DB
	 */
	public String formUpdateOne(Map<String, Object> data,Object id, Map<String, Object> context
			, Map<String, Object> interParm, JSONObject oadb) throws Exception {
//		构造Body
		JSONObject json = getFormBody(data,id,context,oadb);
//		接口调用
		String url = URL + "/seeyon/rest/form/update";
		Map<String, Object> head = new HashMap<>();
		final Object token = interParm.get("token");
		if(ObjUtils.isBlank(token)) throw new Exception("parm中token为空");
		head.put("token", token.toString());
		String ret = RestServiceUtils.put(url,head,json.toJSONString());
//		校验返回
		if (!ret.equals("1"))
			throw new Exception("OA无流程表单导入失败:" + ret);
		return ret;
	}

	/**
	 * 通过sql直接修改OA底表数据
	 * 支持批量
	 * @param datas      数据集合
	 * @param oadb       数据库链接
	 * @param context    相关参数
	 * @param entityInfo 实体信息
	 * @return datas
	 */
	@ServiceLog("OA无流程表单SQL修改")
	public List<Object> formUpdateDirect(List<Map<String, Object>> datas, JSONObject oadb, Map<String, Object> context,
			Map<String, Object> entityInfo) throws Exception {
		Map<String, Object> oamap = (Map<String, Object>) context.get("oamap");
		ExceptionUtils.checkBlankOrUndefined(oamap, "OA字段映射[context.oamap]为空");
		String formname = StrUtil.toString(context.get("formname"));
		String codefield = StrUtil.toString(context.get("codefield"));
		List<Map<String, Object>> transdatas = datas;
//		数据翻译器
		Object transmap = context.get("transmap");
		if (ObjectUtil.isNotEmpty(transmap)) {
			transdatas = transUtils.DataTrans(datas, (Map<String, Object>) transmap, oadb);
		}
//		字段转换,中文->FieldXXX
		transdatas = transservice.transObjectValuetoKey(transdatas, oamap);
		// 通过sql直接更新
		SQLUtils.mapUpdate(transdatas, oadb, formname, ObjUtils.toList(codefield) , null, null);
//		记录实体信息
		for (Map<String, Object> data : datas) {
			entityInfo.put("oper", "修改");
			infoService.insertAitEntityInfo(entityInfo, data, null);
		}
		return Arrays.asList(datas.toArray(new Object[0]));
	}

	/**
	 * OA流程表单发起
	 * 
	 * @param datas      agg数据集合
	 * @param entityName 模板编码
	 * @param interParm  接口地址
	 * @param maintable  主表名
	 * @param sontable   子表名
	 * @param entityInfo 实体记录映射
	 * @return 接口返回值集合
	 */
//	@ServiceLog("OA流程表单发起")
	public List<String> processStart(List<AggMap> datas, String entityName, Map<String, Object> interParm,
			String maintable, String sontable, Map<String, Object> entityInfo, Map<String, Object> context,
			JSONObject oadb) throws Exception {
		List<String> retlist = new ArrayList<>();
//		在processStartOne中会强制刷新token,这里用处不大了
//		this.getToken(interParm,oadb);
		for (AggMap data : datas) {
			try {
				String ret = processStartOne(data, interParm, entityName, maintable, sontable, context, oadb);
				entityInfo.put("oper", "新增");
				infoService.insertAitEntityInfo(entityInfo, data, null);
				retlist.add(ret);
			} catch (Exception ex) {
				entityInfo.put("oper", "新增");
				infoService.insertAitEntityInfo(entityInfo, data, ex);
				retlist.add(ex.getMessage());
//				ex.printStackTrace();
			}
		}
		return retlist;
	}

	/**
	 * 单笔流程发起
	 * 增加同步锁,避免获取token与发起流程存在并发,导致发起人和发起流程的人员不一致问题
	 * @param data       agg数据
	 * @param interParm  接口地址
	 * @param entityName 模板编码
	 * @param maintable  主表名
	 * @param sontable   子表名
	 * @return 接口返回字符串
	 */
	private synchronized String processStartOne(AggMap data, Map<String, Object> interParm, String entityName, String maintable,
			String sontable, Map<String, Object> context, JSONObject oadb) throws Exception {
//		深度Copy,确保翻译器不会修改原始数据
		AggMap data1 = data.clone();

//		从context配置中获取loginField
		if (context.get("loginName") == null)
			throw new Exception("未配置参数 context.loginName");
		final String loginField = context.get("loginName").toString();
//		从data中获取loginName
		if (data.getHeadVO().get(loginField) == null)
			throw new Exception("表头属性不存在字段:" + loginField);
		Object loginName=data.getHeadVO().get(loginField);
//		不能直接修改全局上下文,需要先拷贝
		LinkedHashMap<String, Object> interParm1 = new LinkedHashMap<>(interParm);
		interParm1.put("loginName", loginName);
//		每一条数据,强制刷新token
		final String token1 = this.createToken(interParm1, oadb);
		interParm1.put("token",token1);
//		数据翻译
		dataTranslate(context, oadb, data1);

//		附件处理 新增Context参数attachmentField
		List<String> attachments = AttachmentUpload(context,interParm1, data1);

//		更多子表处理
		List<String> sontableEx=new ArrayList<>();
		if(context.containsKey("sontableEx"))
			sontableEx=(List<String>)context.get("sontableEx");

//		构造OA参数
		Map<String, Object> param = this.getProcessBody(data1, maintable, sontable, entityName,attachments,sontableEx);

//		接口调用
//		这里不能使用this.token,可能会和底表获取token并发,导致获取到root用户
		String url = URL + "/seeyon/rest/bpm/process/start?token=" + token1;
		JSONObject retjson = RestServiceUtils.post(url,null, new JSONObject(param));
//		处理返回
		if (retjson.get("code") == null || !retjson.getString("code").equals("0")) {
			String msg = "OA流程发起报错:" + retjson.getString("message");
			throw new Exception(msg);
		}
		return retjson.toJSONString();
	}

	/**
	 * 对传入OA数据,进行翻译
	 * 用于流程发起的AggMap结构翻译
	 * 主要处理枚举/引用等翻译
	 * 翻译方向 Code->ID
	 * @param context 参数上下文
	 * @param oadb OA DB
	 * @param data 数据Map
	 */
	private AggMap dataTranslate(Map<String, Object> context, JSONObject oadb, AggMap data) throws Exception {
//		数据翻译 data->data1
		Object maintransmap = context.get("maintransmap");
		if (ObjectUtil.isNotEmpty(maintransmap)) {
//			主表翻译器
			List<Map<String, Object>> maintrans = transUtils.DataTrans(Collections.singletonList(data.getHeadVO()),
					(Map<String, Object>) maintransmap, oadb);
			data.setHeadVO(maintrans.get(0));
		}
		Object sontransmap = context.get("sontransmap");
		if (ObjectUtil.isNotEmpty(sontransmap)) {
//			子表翻译器
			List<Map<String, Object>> sontrans = transUtils.DataTrans(data.getBodyVOS(), (Map<String, Object>) sontransmap,
					oadb);
			data.setBodyVOS(sontrans);
		}
		return data;
	}

	/**
	 * 发起流程前,进行附件上传处理,并返回附件url
	 * @param context 参数上下文
	 * @param data 当前发起流程的数据
	 * @return 附件信息,含url
	 */
	private List<String> AttachmentUpload(Map<String, Object> context,Map<String, Object> interParm, AggMap data) throws Exception {
		List<ZYOAFileInfoVO> retFilesInfo=new ArrayList<>();
		List<String> attachments=new ArrayList<>();
		if(context.get("attparms")!=null){
			final Object parmsobj = context.get("attparms");
			JSONObject parmsjson=new JSONObject((Map)parmsobj);
			final String entityName = parmsjson.getString("entityName");
			ObjUtils.checkBlank(entityName,"parms中必须包含entityName");
			final String codeField = parmsjson.getString("codeField");
			ObjUtils.checkBlank(codeField,"parms中必须包含codeField");
			final String fileUrl = parmsjson.getString("fileUrl");
			ObjUtils.checkBlank(fileUrl,"parms中必须包含fileUrl");
			final String redisKey = parmsjson.getString("redisKey");
			ObjUtils.checkBlank(fileUrl,"parms中必须包含redisKey");

			final Object code = data.getHeadVO().get(codeField);
			String key=redisKey+":"+entityName+":"+code;
			final List attlist = redisCache.getCacheObject(key);
			if(ObjUtils.isNotBlank(attlist)){
				//该数据存在附件,进行附件处理
				List<String> paths=new ArrayList<>();
				for (Object attobj : attlist) {
					AttachmentVO attvo=(AttachmentVO) attobj;
					paths.add(attvo.getFilePath());
				}
				final List<ZYOAFileInfoVO> fileInfos = this.fileUpload(data, paths,interParm);
				retFilesInfo.addAll(fileInfos);
			}
		}

		//返回OA附件的ID,及url
		for (ZYOAFileInfoVO vo : retFilesInfo) {
			attachments.add(vo.getFileUrl());
		}
		return attachments;
	}


}
